/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.jfonia.model.quantize;

import java.util.ArrayList;
import org.jfonia.constants.ModelConstants;
import org.jfonia.constants.SymbolConstants;
import org.jfonia.model.PerformanceNode;
import org.jfonia.model.PerformanceSequence;
import org.jfonia.model.Sequence;
import org.jfonia.model.Staff;
import org.jfonia.model.TempoMap;
import org.jfonia.model.TempoNode;
import org.jfonia.model.elements.MusicalElement;
import org.jfonia.model.elements.TimeSignature;
import org.jfonia.model.elements.Tone;
import org.jfonia.staffnotation.BasicTimeSignature;
import percussiongenerator.model.Pair;

/**
 *
 * @author Jannes Plyson
 */
public class NoteOnQuantizer implements IQuantizer{
    private int smallestValue;
    private long gridOffset;
    private Grid grid;
    private long length;
    BasicTimeSignature basicTimeSignature;

    public NoteOnQuantizer(int smallestValue){
        this.smallestValue = smallestValue;
        gridOffset = 0;
        basicTimeSignature = new BasicTimeSignature(4,4);
    }

    public void setSmallestValue(int ticks){
        this.smallestValue = ticks;
    }   

    private ArrayList<Long> getTimes(PerformanceSequence sequence){
        ArrayList<Long> onsetTimes = new ArrayList<Long>();
        for(int i = 0; i < sequence.size(); i++){
            PerformanceNode node = (PerformanceNode)sequence.getIntervalFromIndex(i);            
            onsetTimes.add(node.getBegin());
        }
        onsetTimes.add(sequence.getDifference());//stop point for last note.
        return onsetTimes;
    }

    private long calculateShortestInterval(ArrayList<Long> times){
        long shortestInterval = Long.MAX_VALUE;        
        for(int i = 0; i < times.size()-1; i++){
            shortestInterval = Math.min(shortestInterval, times.get(i+1)-times.get(i));            
        }        
        return shortestInterval;
    }    

    public void updateOffsets(PerformanceSequence sequence,Staff staff){
        ArrayList<Long> onsetTimes = getTimes(sequence);
        long shortestInterval = calculateShortestInterval(onsetTimes);
        if(shortestInterval > 0){
            length = sequence.getDifference();
            grid = new Grid(shortestInterval);
            ArrayList<Pair<Long,Long>> quantizedTimes = grid.fitToGrid(onsetTimes,gridOffset);
            int newStaffLength = getTicks(quantizedTimes.get(quantizedTimes.size()-1).first);
            TimeSignature ts = new TimeSignature(newStaffLength);
            Sequence<TimeSignature> tsSequence = new Sequence<TimeSignature>(TimeSignature.class);
            tsSequence.add(ts);

            int beatLength = SymbolConstants.DIVISOR/basicTimeSignature.getNominator();
            long beatTime = shortestInterval*beatLength/smallestValue;
            int bpm = (int)(ModelConstants.PERFORMANCE_TO_SECOND*60/beatTime);  
            TempoMap tempoMap = null;
            if(staff.getTempoMap() == null){
                tempoMap = new TempoMap();
                TempoNode tempoNode = new TempoNode();
                tempoNode.setBPM(bpm);
                tempoNode.setDifference(newStaffLength);
                tempoMap.add(tempoNode);
            }else{
                tempoMap = staff.getTempoMap();
                TempoNode tempoNode = (TempoNode)tempoMap.getIntervalFromIndex(0);
                while(tempoMap.size() > 1){
                    tempoMap.remove(tempoMap.size()-1);
                }
                tempoNode.setBPM(bpm);
                tempoNode.setDifference(newStaffLength);
            }

            staff.setLength(newStaffLength);
            if(staff.getSequence(TimeSignature.class) == null){
                staff.addSequence(tsSequence);
            }else{
                Sequence<TimeSignature> seq = staff.getSequence(TimeSignature.class);
                while(seq.size() > 1){
                    seq.remove(seq.size()-1);
                }
                TimeSignature ts_ = (TimeSignature)seq.getIntervalFromIndex(0);
                ts_.setNominator(ts.getNominator());
                ts_.setDenominator(ts.getDenominator());
            }
            staff.setTempoMap(tempoMap);

            Sequence notSeq = staff.getSequence(Tone.class);
            if(notSeq.size() > 0){
                for(int i = 0; i < sequence.size(); i++){
                    PerformanceNode node = (PerformanceNode)sequence.getIntervalFromIndex(i);
                    node.getQuantizeOffsetNode().setValue(quantizedTimes.get(i).first-node.getBegin());
                    MusicalElement musicalElement = (MusicalElement)notSeq.getIntervalFromIndex(i);
                    musicalElement.getBeginNode().setValue(getTicks(quantizedTimes.get(i).first));
                }
                MusicalElement musicalElement = (MusicalElement)notSeq.getIntervalFromIndex(notSeq.size()-1);
                musicalElement.setEnd(getTicks(quantizedTimes.get(quantizedTimes.size()-1).first));
            }
        }

    }

    public int getTicks(long time) {
        if(grid != null){            
            return (int)(grid.getGridValue(time, gridOffset)*smallestValue);
        }else{
            return -1;
        }
    }

    public int getSmallestValue() {
        return smallestValue;
    }

}
